package org.jeecg.common.datasource.aop;

import com.zaxxer.hikari.HikariDataSource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecg.common.datasource.annotation.SelectDS;
import org.jeecg.common.datasource.config.DataSourceConfig;
import org.jeecg.common.exception.JeecgBootException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 多数据源切面
 */
@Component
@Aspect
public class DataSourceAspect {
    private static final Logger LG = LoggerFactory.getLogger(DataSourceAspect.class);
    private static final String FORP5_DATATEMPLATE_KEY = "jdbc";
    private static final String MYBATIS_DATATEMPLATE_KEY = "sqlSessionTemplate";
    private volatile Map<String, Object> dataTemplateMap = new ConcurrentHashMap<>();

    @Resource
    private DataSourceConfig dataSourceConfig;

    @Pointcut("@annotation(org.jeecg.common.datasource.annotation.SelectDS)")
    private void pointCut(){

    }

    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        Object objBean = joinPoint.getTarget();
        Class<?> targetClass = objBean.getClass();
        // 获取类上的自定义注解
        SelectDS annotation = targetClass.getAnnotation(SelectDS.class);
        if(annotation == null){
            return;
        }
        String dataSourceName = annotation.name();
        HikariDataSource hikariDataSource = dataSourceConfig.getDbList().get(dataSourceName);
        try{
            if(hikariDataSource == null){
                throw new JeecgBootException("【数据库】动态切换数据源失败, 未能找到数据源：" + dataSourceName);
            }
            if(this.hasBeanWithProperty(objBean, FORP5_DATATEMPLATE_KEY)){
                // 兼容forp6中BaseDao方式
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(objBean);
                JdbcTemplate beanJdbc = (JdbcTemplate)bw.getPropertyValue("jdbc");
                JdbcTemplate jdbcTemplate = (JdbcTemplate)dataTemplateMap.get(dataSourceName);
                if(jdbcTemplate == null || beanJdbc != jdbcTemplate){
                    jdbcTemplate = new JdbcTemplate();
                    jdbcTemplate.setDataSource(hikariDataSource);
                    dataTemplateMap.put(dataSourceName, jdbcTemplate);
                }
                bw.setPropertyValue("jdbc", jdbcTemplate);
            }
        } catch (Exception e){
            throw new JeecgBootException(e);
        }
    }

    public boolean hasBeanWithProperty(Object bean, String propertyName) {
        Field[] fields = bean.getClass().getFields();
        for (Field field : fields) {
            if (field.getName().equalsIgnoreCase(propertyName)) {
                return true;
            }
        }
        return false;
    }
}
